package mt.server;

import java.util.LinkedList;
import java.util.Random;

import mt.Order;
import mt.comm.ServerComm;
import mt.comm.ServerSideMessage;
import mt.comm.ServerSideMessage.Type;
import mt.dummy.ServerCommDummy;



public class MicroTraderServerDummy implements MicroTraderServer {

	private LinkedList<String> clients = new LinkedList<String>();
	private LinkedList<String> clientsOnline = new LinkedList<String>();
	private LinkedList<Order> sellingOrders = new LinkedList<Order>();
	private LinkedList<Order> buyingOrders = new LinkedList<Order>();
	private ServerCommDummy serverComm1;
	private int ordemID=1;	
	/**
	 * Starts the server invoked by serverComm.
	 * 
	 * @param serverComm
	 * 
	 */

	@Override
	public void start(ServerComm serverComm) {
		this.serverComm1 =  (ServerCommDummy) serverComm;
		while(true) {
				ServerSideMessage message = serverComm1.getNextMessage();
				sortMessage(message);
		}
	}

	/**
	 * 
	 * Method that handles the messages according to their type. They might be
	 * new purchase/sale orders or connection/disconnection requests to the
	 * server.
	 * 
	 * @param message
	 */

	public void sortMessage(ServerSideMessage message) {

		if(message.getType().equals(Type.NEW_ORDER)) {
			treatOrder(message.getOrder());
		}
		if(message.getType().equals(Type.DISCONNECTED)){
			clientsOnline.remove(message.getSenderNickname());
		}
		if(message.getType().equals(Type.CONNECTED)){
			treatClient(message.getSenderNickname());
		}
		
	
		
		
	}

	/**
	 * Method that takes a buy or sell order and checks if it is possible or not
	 * to satisfy the request. If not, it stores the order in the respective
	 * list. If yes, it handles the transactions.
	 * 
	 * @param order
	 */

	private synchronized void treatOrder(Order order) {
		order.setServerOrderID(ordemID);
		ordemID++;
		
		if(order.isBuyOrder()){
			buyingOrders.add(order);
			treatBuyOrder(order);
		}
		if(order.isSellOrder()){
			sellingOrders.add(order);
			treatSellOrder(order);
		}
	}

	private void treatSellOrder(Order order) {
		updateClients(order);
		
		for(int i=0;i<buyingOrders.size();i++){
			if(order.getStock().equals(buyingOrders.get(i).getStock()) && order.getPricePerUnit()== buyingOrders.get(i).getPricePerUnit()){
				if(order.getNumberOfUnits()== buyingOrders.get(i).getNumberOfUnits()){
				
					removeOrder(order);
					removeOrder(buyingOrders.get(i));
										
				}
				else if (order.getNumberOfUnits() > buyingOrders.get(i).getNumberOfUnits()) {
					
					int novo=order.getNumberOfUnits()-buyingOrders.get(i).getNumberOfUnits();
					buyingOrders.remove(buyingOrders.get(i));
					sellingOrders.remove(order);
					order.setNumberOfUnits(novo);
					sellingOrders.add(order);
					
				}
				else{

					buyingOrders.get(i).setNumberOfUnits(buyingOrders.get(i).getNumberOfUnits()- order.getNumberOfUnits());
					sellingOrders.remove(order);
					
					
					
				}
					
				
				
			}
		}
		
	}

	private void treatBuyOrder(Order order) {
		updateClients(order);
		
		
		for(int i=0;i<sellingOrders.size();i++){
			if(order.getStock().equals(sellingOrders.get(i).getStock()) && order.getPricePerUnit()== sellingOrders.get(i).getPricePerUnit()){
				if(order.getNumberOfUnits()== sellingOrders.get(i).getNumberOfUnits()){
					

					
					removeOrder(order);
					removeOrder(sellingOrders.get(i));
					
					
				}
				
				else	if (order.getNumberOfUnits() > sellingOrders.get(i).getNumberOfUnits()) {
					
					int novo=order.getNumberOfUnits()-sellingOrders.get(i).getNumberOfUnits();
					sellingOrders.remove(sellingOrders.get(i));
					buyingOrders.remove(order);
					order.setNumberOfUnits(novo);
					buyingOrders.add(order);

				}
				else{
				
					sellingOrders.get(i).setNumberOfUnits(sellingOrders.get(i).getNumberOfUnits()- order.getNumberOfUnits());
					buyingOrders.remove(order);
					
				}
			}
		}
		
		
	}

	

	private void updateClients(Order order) {
		
		for(String string: clientsOnline){
			serverComm1.sendOrder(string, order);
		}
		
	}

	private void removeOrder(Order order) {
		if(order.isBuyOrder()){
			buyingOrders.remove(order);
		}
		else{
			sellingOrders.remove(order);
			
		}
		
	}
		
		
	

	/**
	 * Method that takes a nickname of a specific client. If this client is
	 * registered, it is added to the online customer list. If he is a new
	 * customer, he is added to the list of online and registered users,
	 * furthermore also receiving an update of the list of sales and purchases.
	 * 
	 * @param nickname
	 */
	private void treatClient(String nickname) {
		
		if(clients.size()==0){
			clients.add(nickname);
			clientsOnline.add(nickname);
			System.out.println("Cliente "+ nickname + " adicionado a lista de utilizadores online" );
			System.out.println("Lista tem " + clientsOnline.size() +" utilizadores");
			
			
		}
		else{
			for(int i=0;i<clients.size();i++){
				if(nickname.equals(clients.get(i).trim())){
				
					
					
					for(int j=0;j<clientsOnline.size();j++){
						if(nickname.equals(clientsOnline.get(j).trim())){
							serverComm1.sendError(nickname, "Can't connect: Nickname is being used right now");
							serverComm1.disconnectClient(nickname);
						
						}
						else{
							clientsOnline.add(nickname);
							updateclient(nickname);
							System.out.println("Apenas adicionado a lista de users Online");
						}
					
				}
				
			}
				else{
					
					clients.add(nickname);
					clientsOnline.add(nickname);
					updateclient(nickname);
					System.out.println("Cliente "+ nickname + " adicionado a lista de utilizadores online" );
					System.out.println("Lista tem " + clientsOnline.size() +" utilizadores");	
					break;
					
				}
					
			}
		}
		

			
	}

	private void updateclient(String nickname) {
		for(Order entry: buyingOrders){
			serverComm1.sendOrder(nickname, entry);
		}
		for(Order order: sellingOrders){
			serverComm1.sendOrder(nickname, order);
		}
		
	}
			
	

	

	
}

